try:
    import colorama
    colorama.init()
except:
    pass
import logging,sys,os,io,traceback
from collections import namedtuple
from . import currentframe,find_caller,set_method
import types

def findCaller(self, stack_info=False):
    """
    Find the stack frame of the caller so that we can note the source
    file name, line number and function name.
    """
    f = logging.currentframe()
    #On some versions of IronPython, currentframe() returns None if
    #IronPython isn't run with -X:Frames.
    if f is not None:
        f = f.f_back
    rv = "(unknown file)", 0, "(unknown function)", None
    while hasattr(f, "f_code"):
        co = f.f_code
        filename = os.path.normcase(co.co_filename)
        if filename in (logging._srcfile,os.path.normcase(__file__)):
            f = f.f_back
            continue
        sinfo = None
        if stack_info:
            sio = io.StringIO()
            sio.write('Stack (most recent call last):\n')
            traceback.print_stack(f, file=sio)
            sinfo = sio.getvalue()
            if sinfo[-1] == '\n':
                sinfo = sinfo[:-1]
            sio.close()
        rv = (co.co_filename, f.f_lineno, co.co_name, sinfo)
        break
    return rv



def logger_patch(logger=None):
    if logger is None:
        logger=default_logger()
    if isinstance(logger,logging.Logger):
        set_method(logger,'findCaller',findCaller)
    return logger
class Wrapper(object):
    def __init__(self,logger,name=None,level=logging.DEBUG):
        #~ print(logger,type(logger))
        assert(isinstance(logger,logging.Logger))
        self.logger=logger

        if name is None:
            self.level=level
            self.name=logging._levelToName[level].lower()
        else:
            self.name=name
            if name == 'fatal':
                name='critical'
            if name == 'exception':
                name='error'
            if name == 'print':
                name='info'
                #~ print(logging._nameToLevel)
            self.level=logging._nameToLevel[name.upper()]
    def __call__(self,*args):
        #~ print(self.logger.name)
        string=' '.join( [str(i) for i in args])
        #~ print(string)
        #~ string=self.name + 'xx ' +string
        f=getattr(self.logger,logging._levelToName[self.level].lower(),None)
        #~ print('.'*8,f)
        if f is not None:
            #~ print('x'*8,f)
            f(string)
            #~ print('x'*8,f)
        else:
            self.logger.log(self.level,string)

def create_module_logger(module=None):
    assert(module is None or isinstance(module,types.ModuleType))
    if module is None:
        module =find_caller().module

    if hasattr(module,'logger'):
        return module.logger
    logger = logging.getLogger(module.__name__)
    logger.setLevel(logging.DEBUG)
    #~ print(logger.hasHandlers(),logger.handlers)
    if logger.hasHandlers():
        return logger
    # create console handler and set level to debug
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    # create formatter
    formatter = ColorFormatter('%(name)s:%(filename)s:%(funcName)s:%(lineno)d:%(message)s')
    # add formatter to ch
    ch.setFormatter(formatter)
    # add ch to logger
    logger.addHandler(ch)
    setattr(module,'logger',logger)
    return logger

def patch_module(module=None):
    assert(module is None or isinstance(module,types.ModuleType))
    if module is None:
        module =find_caller().module
    logger=create_module_logger(module=module)
    ret={}
    for name,func in wrap_logger(logger):
        setattr(module,name,func)
        ret[name]=func
    return ret

def wrap_logger(logger=None,funcs=['debug','warn','info','critical','error','fatal','exception','print']):
    logger=logger_patch(logger)
    for i in funcs:
        yield i,Wrapper(logger,name=i)
    #~ return ret
import collections
def install(path=None):
    assert(path is None or isinstance(path, collections.Iterable))
    from . import register_override as override
    if path is None:
        path=['.']
    for i in path:
        override(i,patch_module)
    #~ pass

import enum
class LoggingCols(enum.Enum):
    @classmethod
    def validate(cls,v):
        if isinstance(v,cls):
            return v
        if type(v) is not str:
            raise ValueError(repr(v) + ' is not instance of ' + str(cls)
                        + 'and it is not a str object')
        try:
            return cls(v)
        except ValueError as e:
            if hasattr(cls,v):
                return getattr(cls,v)
            return False
    msecs            = '%(msecs)d'
    lineno           = '%(lineno)d'
    module           = '%(module)s'
    filename         = '%(filename)s'
    name             = '%(name)s'
    funcName         = '%(funcName)s'
    pathname         = '%(pathname)s'
    process          = '%(process)d'
    levelno          = '%(levelno)s'
    asctime          = '%(asctime)s'
    created          = '%(created)f'
    levelname        = '%(levelname)s'
    message          = '%(message)s'
    relativeCreated  = '%(relativeCreated)d'
    thread           = '%(thread)d'
    threadName       = '%(threadName)s'

class ColorFormatter(logging.Formatter):
    def __init__(self, cols=[LoggingCols.name,LoggingCols.levelname,LoggingCols.filename,LoggingCols.lineno,LoggingCols.message]):
        if type(cols) is str:
            cols=cols.split(':')
        cols=list(map(lambda i:LoggingCols.validate(i),cols))
        assert(all(cols))
        colors=list(range(31,37))
        fmt=[]
        for i in range(len(cols)):
            _fmt='\033[%dm' % colors[i%len(colors)]
            _fmt+=cols[i].value
            fmt.append(_fmt)
        super(ColorFormatter, self).__init__(':'.join(fmt) + '\033[0m')

    def format(self, record):

        return super(ColorFormatter, self).format(record)
